home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / Libraries / Dots & Pixels / toolsources / Differential main.cp next >
Text File  |  1995-09-29  |  34KB  |  1,203 lines

  1. #include <assert.h>
  2. #include <ctype.h>
  3. #include <math.h>
  4. #include <stdlib.h>
  5. #include <string.h>
  6.  
  7. #include <fstream.h>
  8. #include <iostream.h>
  9. #include <strstream.h>
  10.  
  11. #include <Fonts.h>
  12. #include <Menus.h>
  13. #include <Packages.h>
  14. #include <SegLoad.h>
  15. #include <ToolUtils.h>
  16. #include <TextEdit.h>
  17. #include <GestaltEqu.h>
  18. #include <Windows.h>
  19. #include <QuickDraw.h>
  20. #include <QDOffscreen.h>
  21. #include <Palettes.h>
  22. #include <Resources.h>
  23. #include <Timer.h>
  24. #include <Retrace.h>
  25. #include <Devices.h>
  26.  
  27. #include "general.h"
  28. #include "application.h"
  29. #include "port.h"
  30. #include "window.h"
  31. #include "depthChange.h"
  32. #include "fullscreen.h"
  33. #include "C_randomizer.h"
  34. #include "stopwatch.h"
  35. #include "macutilities.h"
  36. #include "vretrace.h"
  37.  
  38. #include "streamutils.h"
  39.  
  40. #include "outputfile.h"
  41. #include "flowsettings.h"
  42. #include "phaser.h"
  43. #include "dotcollection.h"
  44. #include "screenarea.h"
  45. #include "screendots.h"
  46. #include "flowdots.h"
  47.  
  48. #ifndef __CONDITIONALMACROS__
  49.     #define SelectDialogItemText    SelIText
  50.     #define GetDialogItem            GetDItem
  51.     #define GetDialogItemText        GetIText
  52.     #define SetDialogItemText        SetIText
  53.     #define SetControlValue            SetCtlValue
  54.     #define GetControlValue            GetCtlValue
  55.     #define TETextBox                TextBox
  56.     #define ShowDialogItem            ShowDItem
  57.     #define HideDialogItem            HideDItem
  58. #endif
  59. //
  60. // use_movingnoise is a hack to allow one to study moving noise
  61. //
  62. #define use_movingnoise
  63.  
  64. #ifdef use_movingnoise
  65.     #define noisedots movingnoisedots
  66.     #include "movingnoisedots.h"
  67. #else
  68.     #include "noisedots.h"
  69. #endif
  70. //
  71. #define acceleration_factor 5.0
  72.  
  73. #include "Differential main.h"
  74.  
  75. int step_once( flowdots &alive, flowdots &kicking,
  76.             noisedots &alive_noise, noisedots &kicking_noise,
  77.             double d_log, double d_rot, double d_trans, Boolean switched  = false);
  78.  
  79. // void main();
  80. void one_loop( opts *the_opts, flowsettings &alive_settings, flowsettings &kicking_settings);
  81. //
  82. // the default options get passed because some of the options are not settable
  83. // in the dialog. Thus, they do not get passed via the path
  84. //            default_options => dialog => options,
  85. // but must be passed directly from the default options to the options.
  86. //
  87. void Dialog2options( const DialogPtr theDialog,
  88.             const opts *default_options, opts *the_options);
  89.  
  90. void options2Dialog( const opts *the_options, DialogPtr theDialog);
  91. void FlipItem( DialogPtr myDialog, short theItem);
  92.  
  93. short GetItemValue( const DialogPtr myDialog, short theItem);
  94. void SetItemValue( DialogPtr myDialog, short theItem, short value);
  95.  
  96. long GetItemLong( const DialogPtr myDialog, short theItem);
  97. void SetItemLong( DialogPtr myDialog, short theItem, long theValue);
  98.  
  99. void UpdateSensitivityItems( DialogPtr theDialog, long allow_modifications);
  100.  
  101. void showParams( const flowdots &alive, const flowdots &kicking, short xPos);
  102.  
  103. void showLegends( short xPos);
  104. //
  105. // DrawNumber does for numbers what DrawString does for strings. As a bonus
  106. // it can draw numbers with an implicit decimal point. implicitDecimal
  107. // may be anything between zero and eight, inclusive.
  108. // DrawNumber with a double parameter draws with three digits after the decimal point.
  109. //
  110. void DrawNumber( long theNumber, int implicitDecimal = 0);
  111. void DrawNumber( double theNumber);
  112.  
  113. int main( int argc, char *argv[])
  114. {
  115.     application me;
  116.     //
  117.     // The background screen is included to force a restore of some
  118.     // useful palette when the main dialog is made visible again.
  119.     // The stimulus generation really messes up the display, and uses
  120.     // an 8-bit fullscreen. By creating a four-bit one here we force the
  121.     // desctructor of the stimulus fullscreen to switch back to four-bit
  122.     // mode, thus giving us a color table we can live with.
  123.     // The alternative of simply creating and immediately disposing of a
  124.     // fullscreen of 1, 2, or 4 bits does work, too, but is even more
  125.     // awful to watch (it flashes black-white-black-almost white).
  126.     // This won't work if the main device doesn't support 4-bit mode, and
  127.     // we do not check for that, but there already are plenty of other
  128.     // things we do not check for.
  129.     // (e.g. 68030+, FPU, color QuickDraw,…)
  130.     //
  131.     fullscreen background( 4, (unsigned char)false);
  132.  
  133.     Handle theResource = Get1Resource( 'pref', 128);
  134.     assert( theResource != 0);
  135.     assert( (*theResource) != 0);
  136.     opts *default_opts = (opts *)*theResource;
  137.     
  138.     opts *the_opts = new opts;
  139.  
  140.     DialogRecord    myDialogRecord;    
  141.     DialogPtr        myDialog =
  142.             GetNewDialog( 128, &myDialogRecord, (WindowPtr)-1);
  143.     if( myDialog == 0)
  144.     {
  145.         DebugStr( "\pCould not get the dialog!");
  146.         exit( EXIT_FAILURE);
  147.     }
  148.     short itemHit = 0;
  149.     
  150.     options2Dialog( default_opts, myDialog);
  151.     SelectDialogItemText( myDialog, iNumDots1, 0, 32767);
  152.  
  153.     flowsettings alive_settings;
  154.     flowsettings kicking_settings;
  155.  
  156.     do
  157.     {
  158.         #ifdef __MWERKS__
  159.         {
  160.             depthChange een( 1);
  161.             depthChange twee( 2);
  162.             depthChange vier( 4);
  163.             depthChange acht( 8);
  164.         }
  165.         #endif
  166.         ModalDialog( 0L, &itemHit);
  167.         switch( itemHit)
  168.         {
  169.             case iRun:
  170.                 Dialog2options( myDialog, default_opts, the_opts);
  171.                 one_loop( the_opts, alive_settings, kicking_settings);
  172.                 FlushEvents( everyEvent, 0L);
  173.                 break;
  174.  
  175.             case iQuit:
  176.                 break;
  177.  
  178.             case iDefaults:
  179.                 options2Dialog( default_opts, myDialog);
  180.                 SelectDialogItemText( myDialog, iNumDots1, 0, 32767);
  181.                 break;
  182.  
  183.             case iNumDots1:
  184.             case iNumDots2:
  185.             case iNumNoiseDots1:
  186.             case iNumNoiseDots2:
  187.  
  188.             case iLife1:
  189.             case iLife2:
  190.             case iNoiseLife1:
  191.             case iNoiseLife2:
  192.             case iDiffusion1:
  193.             case iDiffusion2:
  194.  
  195.             case iSwitchInterval:
  196.  
  197.             case i_dLog:
  198.             case i_dTrans:
  199.             case i_dRotDir:
  200.                 DebugStr( "\pThat item should not be enabled");
  201.                 break;
  202.                 
  203.             case iTransparant:
  204.             case iAnnulus:
  205.             case iHalves:
  206.             case iQuarts:
  207.             case iEighths:
  208.             case iCheckers8:
  209.             case iCheckers16:
  210.             case iSurprise:
  211.                 {
  212.                     short  itemType;
  213.                     Handle itemHandle;
  214.                     Rect   itemBox;
  215.                     for( int itemNo = iTransparant; itemNo <= iSurprise; itemNo++)
  216.                     {
  217.                         GetDialogItem( myDialog, itemNo,
  218.                                 &itemType, &itemHandle, &itemBox);
  219.                         SetControlValue( (ControlHandle)itemHandle, (itemNo == itemHit));
  220.                     }
  221.                 }
  222.                 break;
  223.  
  224.             case iFixationDot:
  225.             case iLogClicks:
  226.             case iCircularMask:
  227.             case iFlickering:
  228.             case iStepThrough:
  229.                 FlipItem( myDialog, itemHit);
  230.                 break;
  231.             
  232.             case i128x128:
  233.             case i256x256:
  234.             case i512x512:
  235.             case i1024x1024:
  236.                 {
  237.                     short  itemType;
  238.                     Handle itemHandle;
  239.                     Rect   itemBox;
  240.                     for( int itemNo = i128x128; itemNo <= i1024x1024; itemNo++)
  241.                     {
  242.                         GetDialogItem( myDialog, itemNo,
  243.                                 &itemType, &itemHandle, &itemBox);
  244.                         SetControlValue( (ControlHandle)itemHandle, (itemNo == itemHit));
  245.                     }
  246.                 }
  247.                 break;
  248.             
  249.             case iAllowModifications:
  250.                 FlipItem( myDialog, itemHit);
  251.                 UpdateSensitivityItems( myDialog,
  252.                             GetItemValue( myDialog, iAllowModifications));
  253.                 break;
  254.  
  255.             default:
  256.                 DebugStr( "\pHit an item I haven't been told to exist!");
  257.         }
  258.     } while( itemHit != iQuit);
  259.     ReleaseResource( theResource);
  260.     CloseDialog( myDialog);
  261.     delete the_opts;
  262.  
  263.     return EXIT_SUCCESS;
  264. }
  265.  
  266. void one_loop( opts *the_opts, flowsettings &alive_settings, flowsettings &kicking_settings)
  267. {
  268.     //
  269.     // 'clut' 100 contains 256 entries and is divided into four equal parts
  270.     //  which all start with black.
  271.     //
  272.     //   0- 63    contains a pattern which is so that '64' stays black when adding
  273.     //            up to seven times '8', and becomes white when adding '1' up to seven
  274.     //            times, and adding '1' dominates adding '8'.
  275.     //
  276.     //  64-127    contains a pattern which is so that '64' stays black when adding
  277.     //            up to seven times '1', and becomes white when adding '8' up to seven
  278.     //            times, and adding '8' dominates adding '1'.
  279.     //
  280.     // 128-191    contains one black and 63 white entries. Thus, adding up to seven
  281.     //            times '8' and/or up to seven times '1' makes the pattern white.
  282.     //
  283.     // 192-254    contains 64 black entries. => '0' stays black when adding
  284.     //            1 and/or 8, each up to seven times.
  285.     //
  286.     // 255        is a special gray value, used to make legends less obnoxious
  287.     //
  288.     // We draw our pattern using the four indices 0, 64, 128, and 192.
  289.     //
  290.     //   0 = stimulus one visible
  291.     //  64 = stimulus two visible
  292.     // 128 = both stimuli visible
  293.     // 192 = always black
  294.     //
  295.     CTabHandle theColorTable = (CTabHandle)Get1Resource( 'clut', 100);
  296.  
  297.     fullscreen thescreen( 8, theColorTable, (unsigned char)true);
  298.     Rect screenrect;
  299.     
  300.     thescreen.get_rect( &screenrect);
  301.  
  302.     const int screenHeight = screenrect.bottom - screenrect.top;
  303.     const int screenWidth  = screenrect.right  - screenrect.left;
  304.  
  305.     HideCursor();
  306.  
  307.     const int numdots_1        = (const int)the_opts->numdots_1;
  308.     const int numdots_2        = (const int)the_opts->numdots_2;
  309.     const int num_noise_1    = (const int)the_opts->num_noise_1;
  310.     const int num_noise_2    = (const int)the_opts->num_noise_2;
  311.     
  312.     const int lifetime_1    = (const int)the_opts->lifetime_1;
  313.     const int lifetime_2    = (const int)the_opts->lifetime_2;
  314.     const int noise_life_1    = (const int)the_opts->noise_life_1;
  315.     const int noise_life_2    = (const int)the_opts->noise_life_2;
  316.  
  317.     const int noise_diffusion_1 = (const int)the_opts->diffusion_1;
  318.     const int noise_diffusion_2 = (const int)the_opts->diffusion_2;
  319.  
  320.     const int switch_interval = (const int)the_opts->switchinterval;
  321.  
  322.     const int stimType        = the_opts->stim_type + iTransparant;
  323.     const int numBits        = the_opts->resolution_type;
  324.  
  325.     const int do_output        = (the_opts->create_output != 0);
  326.     const int fixation_dot    = (the_opts->fixation_dot != 0);
  327.     const int round_frame    = (the_opts->round_frame != 0);
  328.     const int flickering    = (the_opts->flickering != 0);
  329.  
  330.     const double d_log       = ((double)the_opts->d_log)        / 1000.0;
  331.     const double d_rot      = ((double)the_opts->d_rot_dir)    / 1000.0;
  332.     const double d_trans     = ((double)the_opts->d_trans)    / 1000.0;
  333.  
  334.     const int num_timingloops = the_opts->numtimingloops;
  335.  
  336.     const Boolean stepping    = the_opts->stepping;
  337.     
  338.     ofstream *outfile = 0;
  339.     if( do_output)
  340.     {
  341.         //
  342.         // 941118: outputfile::outputfile creates a Macintosh file, but doesn't open
  343.         // it. This way we can have the best of both worlds, with outputfile::outputfile
  344.         // setting creator and type of the file, and yet we can use iostream functions
  345.         // to format output.
  346.         //
  347.         outputfile outfileName( "Differential out");
  348.         outfile =
  349.             #ifdef __MWERKS__
  350.                 new ofstream( outfileName(), ios::app | ios::out);
  351.             #else
  352.                 new ofstream( outfileName(), ios::translated | ios::app | ios::out);
  353.             #endif
  354.     }
  355.     const int size      = (1 << numBits);
  356.     const int half_size = size / 2;
  357.  
  358.     const int half_screenHeight = screenHeight / 2;
  359.     const int half_screenWidth  = screenWidth  / 2;
  360.     PicHandle the_pict = (PicHandle)Get1Resource( 'PICT', 128);
  361.     const short pict_height = (*the_pict)->picFrame.bottom - (*the_pict)->picFrame.top;
  362.     const short pict_width  = (*the_pict)->picFrame.right  - (*the_pict)->picFrame.left;
  363.  
  364.     const int xpos = max( 0, half_screenWidth  - half_size - pict_width);
  365.     const int ypos = half_screenHeight - half_size;
  366.  
  367.     flowdots alive(   numBits, xpos, ypos, numdots_1, lifetime_1);
  368.     flowdots kicking( numBits, xpos, ypos, numdots_2, lifetime_2);
  369.     
  370.     alive.setsettings( alive_settings);
  371.     kicking.setsettings( kicking_settings);
  372.  
  373.     #ifdef use_movingnoise
  374.         movingnoisedots alive_noise( numBits, xpos, ypos,
  375.                 num_noise_1, noise_life_1, noise_diffusion_1);
  376.         movingnoisedots kicking_noise( numBits, xpos, ypos,
  377.                 num_noise_2, noise_life_2, noise_diffusion_2);
  378.     #else
  379.         noisedots alive_noise( numBits, xpos, ypos, num_noise_1, noise_life_1);
  380.         noisedots kicking_noise( numBits, xpos, ypos, num_noise_2, noise_life_2);
  381.     #endif
  382.     const Rect stimulusrect = { ypos, xpos, ypos + size, xpos + size};
  383.  
  384.     PmForeColor( 192);
  385.     PaintRect( &stimulusrect);
  386.     if( round_frame)
  387.     {
  388.         RgnHandle theClip = NewRgn();
  389.         OpenRgn();
  390.         Rect ovalRect = stimulusrect;
  391.         if( ovalRect.top < 0)
  392.         {
  393.             InsetRect( &ovalRect, -ovalRect.top, -ovalRect.top);
  394.         }
  395.         FrameOval( &ovalRect);
  396.         CloseRgn( theClip);
  397.         SetClip( theClip);
  398.         DisposeRgn( theClip);
  399.     }
  400.     switch( stimType)
  401.     {
  402.         case iTransparant:
  403.             PmForeColor( 128);
  404.             PaintRect( &stimulusrect);
  405.             break;
  406.  
  407.         case iAnnulus:
  408.             {
  409.                 PmForeColor( 0);
  410.                 PaintRect( &stimulusrect);
  411.                 Rect ovalrect = stimulusrect;
  412.                 //
  413.                 // Note: on a 640x480 screen the circular annulus does not fill half the
  414.                 // stimulus area since 32 scanlines (512-32) are dropped at the top and
  415.                 // bottom edges of the screen.
  416.                 //
  417.                 const int the_inset = (size * 76L) / 512L; // 256 - 256 / √2 ≈ 76
  418.                 InsetRect( &ovalrect, the_inset, the_inset);
  419.                 PmForeColor( 64);
  420.                 PaintOval( &ovalrect);
  421.             }
  422.             break;
  423.             
  424.         case iHalves:
  425.             {
  426.                 Rect left_half  = stimulusrect;
  427.                 left_half.right = (stimulusrect.left + stimulusrect.right) / 2;
  428.                 PmForeColor( 64);
  429.                 PaintRect( &left_half);
  430.                 Rect right_half = stimulusrect;
  431.                 right_half.left = left_half.right;
  432.                 PmForeColor( 0);
  433.                 PaintRect( &right_half);
  434.             }
  435.             break;
  436.  
  437.         case iQuarts:
  438.             {
  439.                 Rect a_quart = stimulusrect;
  440.                 a_quart.right  = (stimulusrect.left + stimulusrect.right ) / 2;
  441.                 a_quart.bottom = (stimulusrect.top  + stimulusrect.bottom) / 2;
  442.                 PmForeColor( 0);
  443.                 PaintRect( &a_quart);
  444.  
  445.                 OffsetRect( &a_quart, half_size, 0);
  446.                 PmForeColor( 64);
  447.                 PaintRect( &a_quart);
  448.  
  449.                 OffsetRect( &a_quart, 0, half_size);
  450.                 PmForeColor( 0);
  451.                 PaintRect( &a_quart);
  452.  
  453.                 OffsetRect( &a_quart, -half_size, 0);
  454.                 PmForeColor( 64);
  455.                 PaintRect( &a_quart);
  456.             }
  457.             break;
  458.  
  459.         case iEighths:
  460.             {
  461.                 Rect bigRect = stimulusrect;
  462.                 InsetRect( &bigRect, -5000, -5000);
  463.                 int currentcolor = 0;
  464.                 for( int i = 0; i < 8; i++)
  465.                 {
  466.                     PmForeColor( currentcolor);
  467.                     currentcolor = 64 - currentcolor;
  468.                     PaintArc( &bigRect, 45 * i, 45);
  469.                 }
  470.             }
  471.             break;
  472.             
  473.         case iCheckers8:
  474.         case iCheckers16:
  475.             {
  476.                 const int num_checkers = (stimType == iCheckers8) ?  8 : 16;
  477.                 Rect a_rect = stimulusrect;
  478.                 const int checker_size = size / num_checkers;
  479.                 a_rect.right  = a_rect.left + checker_size;
  480.                 a_rect.bottom = a_rect.top  + checker_size;
  481.                 
  482.                 int currentcolor = 0;
  483.                 for( int y = 0; y < num_checkers; y++)
  484.                 {
  485.                     for( int x = 0; x < num_checkers; x++)
  486.                     {
  487.                         PmForeColor( currentcolor);
  488.                         currentcolor = 64 - currentcolor;
  489.                         PaintRect( &a_rect);
  490.                         OffsetRect( &a_rect, checker_size, 0);
  491.                     }
  492.                     currentcolor = 64 - currentcolor;
  493.                     OffsetRect( &a_rect, -size, checker_size);
  494.                 }
  495.             }
  496.             break;
  497.             
  498.         case iSurprise:
  499.             {
  500.                 PmForeColor( 64);
  501.                 PaintRect( &stimulusrect);
  502.                 PmForeColor(  0);
  503.                 PmBackColor( 64);
  504.                 TextSize( size / 5);
  505.                 TextFont( helvetica);
  506.                 TextStyle( bold);
  507.                 TextMode( srcCopy);
  508.                 const char text[] =
  509.                     "\rWritten by\rReinder Verlinde";
  510.                 const int length = sizeof( text) - 1;    // zero terminator
  511.                 TETextBox( text, length, &stimulusrect, teJustCenter);
  512.             }
  513.             break;
  514.             
  515.         default:
  516.             DebugStr( "\pUnimplemented stimulus type. This is a bug!");
  517.     }
  518.     const Rect bigRect = {-32767, -32767, 32767, 32767};
  519.     ClipRect( &bigRect);
  520.     //
  521.     // Add the fixation dot
  522.     //
  523.     if( fixation_dot)
  524.     {
  525.         Rect fixationRect = stimulusrect;
  526.  
  527.         PmForeColor( 1);
  528.         const short the_inset = half_size - 2;                    // leave 4x4 pixels
  529.         InsetRect( &fixationRect, the_inset, the_inset);
  530.         PaintRect( &fixationRect);
  531.     }
  532.     TextMode( srcCopy);
  533.     TextFont( monaco);
  534.     //
  535.     // Note: TextSizes 9 and 12 don't work (maybe because that font is in ROM,
  536.     //         or because it is a bitmapped version of Monaco; I don't see why
  537.     //         either of these should make a difference, but apparently they do)
  538.     //
  539.     TextSize( 10);
  540.  
  541.     const int params_x = screenWidth - 112; // stimulusrect.right + 16;
  542.  
  543.     PmForeColor( 255);
  544.     showLegends( params_x);
  545.     //
  546.     // Draw the key legends
  547.     //    
  548.     Rect pict_rect;
  549.  
  550.     pict_rect.top    = screenHeight - pict_height;
  551.     pict_rect.left   = screenWidth  - pict_width;
  552.     pict_rect.bottom = screenHeight;
  553.     pict_rect.right  = screenWidth;
  554.  
  555.     DrawPicture( the_pict, &pict_rect);
  556.     //
  557.     // The set-up is completed. Compute the display speed.
  558.     //
  559.     const double framerate = framerate_of_main_monitor();
  560.  
  561.     const int num_flicker_frames = stepping ? 60 : 1;
  562.     vretrace flicker( num_flicker_frames);
  563.  
  564.     stopwatch omega;
  565.     int i;
  566.     omega.start();
  567.  
  568.     for( i = 0; i < num_timingloops; i++)
  569.     {
  570.         (void)step_once( alive, kicking,
  571.                 alive_noise, kicking_noise,
  572.                 d_log, d_rot, d_trans);
  573.     }
  574.     const double async_milliseconds =
  575.         stopwatch::milliseconds( omega.stop()) / (double)num_timingloops;
  576.     flicker.start();
  577.     
  578.     (void)flicker.sync();
  579.     omega.start();
  580.     for( i = 0; i < num_timingloops; i++)
  581.     {
  582.         (void)step_once( alive, kicking,
  583.                 alive_noise, kicking_noise,
  584.                 d_log, d_rot, d_trans);
  585.         (void)flicker.sync();
  586.     }
  587.     const double sync_milliseconds =
  588.         stopwatch::milliseconds( omega.stop()) / (double)num_timingloops;
  589.     const int frames_per_update = (int)(sync_milliseconds * framerate / 1000.0 + 0.5);
  590.     //
  591.     // 941121: OOPS! in real life 'how_often' should not be expressed in the duration
  592.     // of the framerate of the monitor, but simply as a frequency, so instead of doing
  593.     //
  594.     //        alive.set_how_often( frames_per_update);
  595.     //        kicking.set_how_often( frames_per_update);
  596.     //
  597.     // We have to do
  598.     //
  599.     alive.set_how_often(   1000.0 / sync_milliseconds);
  600.     kicking.set_how_often( 1000.0 / sync_milliseconds);
  601.     //
  602.     // For the standard settings of this program this makes a difference
  603.     // We have:
  604.     // sync_milliseconds ≈ 45ms (about three frames)
  605.     // frames_per_update = 3
  606.     // 1000.0 / sync_milliseconds ≈ 22
  607.     //
  608.     // This wasn't a real problem since this program has only been used as a demo up till now.
  609.     //
  610.     if( do_output)
  611.     {
  612.         (*outfile) << "Two 'flowdots' instances with "
  613.                 << numdots_1 << " and " << numdots_2 << " dots\n"
  614.                 "frame timing without syncing: "
  615.                 << async_milliseconds << "ms ("
  616.                 << 1000.0 / async_milliseconds << " frames/sec)\n"
  617.                 "frame timing with    syncing: "
  618.                 << sync_milliseconds << "ms ("
  619.                 << 1000.0 / sync_milliseconds << " frames/sec)\n"
  620.                 "monitor frame takes         : "
  621.                 << 1000.0 / framerate << "ms ("
  622.                 << framerate << " frames/sec)\n\n"
  623.                 << frames_per_update << " Vertical blanks per frame\n"
  624.                 << "\nOption settings were:"
  625.                 << "\nd_log   = " << d_log
  626.                 << "\nd_rot   = " << d_rot
  627.                 << "\nd_trans = " << d_trans
  628.                 << "\nstimulus= " << stimType
  629.                 << "\nlife 1  = " << lifetime_1
  630.                 << "\nlife 2  = " << lifetime_2
  631.                 << "\nswitching interval = " << switch_interval
  632.                 << "\n\n";
  633.     }
  634.     int ready = false;
  635.     int button_was_down = false;
  636.     int current_switch = switch_interval;
  637.     
  638.     Boolean switched = false;
  639.     while( !ready)
  640.     {
  641.         ready = step_once( alive, kicking, alive_noise, kicking_noise, d_log, d_rot, d_trans, switched);
  642.         (void)flicker.sync();
  643.  
  644.         if( flickering)
  645.         {
  646.             alive.EraseDots();
  647.             kicking.EraseDots();
  648.             (void)flicker.sync();
  649.         }
  650.         if( switch_interval != 0)
  651.         {
  652.             current_switch -= 1;
  653.             
  654.             if( current_switch == 0)
  655.             {
  656.                 const flowsettings alive_temp   = alive;
  657.                 const flowsettings kicking_temp = kicking;
  658.                 
  659.                 alive.setsettings( kicking_temp);
  660.                 kicking.setsettings( alive_temp);
  661.                 
  662.                 current_switch = switch_interval;
  663.                 switched = !switched;
  664.             }
  665.         }
  666.         
  667.         const int button_is_down = Button();
  668.         
  669.         if( button_is_down && !button_was_down)
  670.         {
  671.             //
  672.             // First show the current flow parameters on the screen
  673.             //
  674.             if( switched)
  675.             {
  676.                 showParams( kicking, alive, params_x);
  677.             } else {
  678.                 showParams( alive, kicking, params_x);
  679.             }
  680.             //
  681.             // Then also print them to the output file
  682.             //
  683.             if( do_output)
  684.             {
  685.                 (*outfile)
  686.                     << (flowsettings &)alive << '\n'
  687.                     << (flowsettings &)kicking
  688.                     << "\n\n" << endl;
  689.             }
  690.         }
  691.         button_was_down = button_is_down;
  692.     }
  693.     //
  694.     // remember current settings
  695.     //
  696.     if( switched)
  697.     {
  698.         alive_settings   = kicking;
  699.         kicking_settings = alive;
  700.     } else {
  701.         alive_settings   = alive;
  702.         kicking_settings = kicking;
  703.     }
  704.     
  705.     ReleaseResource( (Handle)the_pict);
  706.     if( do_output)
  707.     {
  708.         outfile->flush();
  709.         outfile->close();
  710.         delete outfile;
  711.     }
  712.     ShowCursor();
  713.  
  714.     ReleaseResource( (Handle)theColorTable);
  715. }
  716.  
  717. int step_once( flowdots &alive, flowdots &kicking,
  718.             noisedots &alive_noise, noisedots &kicking_noise,
  719.             double d_log, double d_rot, double d_trans, Boolean switched)
  720. {
  721.     alive.make_a_move( 8);
  722.     kicking.make_a_move( 1);
  723.     alive_noise.make_a_move( 8);
  724.     kicking_noise.make_a_move( 1);
  725.     //
  726.     // Compute new flow parameters:
  727.     //
  728.     KeyMap thekeys;
  729.  
  730.     GetKeys( thekeys);
  731.  
  732.     const int key_one = thekeys[ 1];
  733.     const int caps_is_down    = (key_one & 0x00000002) != 0;
  734.     const int shift_is_down   = (key_one & 0x00000001) != 0;
  735.     const int control_is_down = (key_one & 0x00000008) != 0;
  736.     const int escape_is_down  = (key_one & 0x00002000) != 0;
  737.     const int option_is_down  = (key_one & 0x00000004) != 0;
  738.  
  739.     const long the_value = thekeys[ 2];
  740.  
  741.     flowdots *theflow = caps_is_down
  742.             ? (switched ? &alive   : &kicking)
  743.             : (switched ? &kicking : &alive);
  744.     
  745.     if( control_is_down && !shift_is_down)    // finger might slip to control key
  746.     {
  747.         theflow->expansion       = 1.0;
  748.         theflow->rotation        = 0.0;
  749.         theflow->shear_magnitude = 1.0;
  750.         theflow->shear_direction = 0.0;
  751.         theflow->translation_x   = 0.0;
  752.         theflow->translation_y   = 0.0;
  753.         theflow->changed();
  754.     } else {
  755.         if( the_value != 0)
  756.         {
  757.             const double scale = shift_is_down ? acceleration_factor : 1.0;
  758.             const double power_scale = 
  759.                 shift_is_down ? pow( d_log, acceleration_factor) : d_log;
  760.             //
  761.             // Note: the following bitmasks are hard-coded for
  762.             // the Apple Extended keyboard II's numeric keypad.
  763.             //
  764.             if( option_is_down)
  765.             {
  766.                 double div = log( theflow->expansion);
  767.                 double rot = DegreesToRadians( theflow->rotation);
  768.  
  769.                 double r;
  770.                 double fi;
  771.                 CartesianToPolar( div, rot, &r, &fi);
  772.  
  773.                 if( the_value & 0x00080000)    r += power_scale - 1.0;
  774.                 if( the_value & 0x80000000) r -= power_scale - 1.0;
  775.                 if( the_value & 0x00000200)    r = 0.0;
  776.                 if( r < 0.0)
  777.                 {
  778.                     r = 0.0;
  779.                 }
  780.         
  781.                 if( the_value & 0x00000002)    fi += DegreesToRadians( scale);
  782.                 if( the_value & 0x00000010)    fi -= DegreesToRadians( scale);
  783.                 if( the_value & 0x00000008)    fi = 0.0;
  784.                 
  785.                 PolarToCartesian( r, fi, &div, &rot);
  786.  
  787.                 theflow->expansion = exp( div);
  788.                 theflow->rotation  = RadiansToDegrees( rot);
  789.             } else {
  790.                 if( the_value & 0x00080000)    theflow->expansion *= power_scale;
  791.                 if( the_value & 0x80000000)    theflow->expansion /= power_scale;
  792.                 if( the_value & 0x00000200)    theflow->expansion  = 1.00;
  793.         
  794.                 if( the_value & 0x00000002)    theflow->rotation -= d_rot * scale;
  795.                 if( the_value & 0x00000010)    theflow->rotation += d_rot * scale;
  796.                 if( the_value & 0x00000008)    theflow->rotation  = 0.00;
  797.             }
  798.             if( the_value & 0x00000001)    theflow->shear_magnitude *= power_scale;
  799.             if( the_value & 0x00004000)    theflow->shear_magnitude /= power_scale;
  800.             if( the_value & 0x00008000)    theflow->shear_magnitude  = 1.00;
  801.     
  802.             if( the_value & 0x00000800)    theflow->shear_direction -= d_rot * scale;
  803.             if( the_value & 0x00002000)    theflow->shear_direction += d_rot * scale;
  804.             if( the_value & 0x00001000)    theflow->shear_direction  = 0.00;
  805.  
  806.             if( option_is_down)
  807.             {
  808.                 double dx = theflow->translation_x;
  809.                 double dy = theflow->translation_y;
  810.  
  811.                 double r;
  812.                 double fi;
  813.                 CartesianToPolar( dx, dy, &r, &fi);
  814.  
  815.                 if( the_value & 0x00000400)    r -= d_trans * scale;
  816.                 if( the_value & 0x00100000) r += d_trans * scale;
  817.                 if( the_value & 0x02000000)    r = 0.0;
  818.                 if( r < 0.0)
  819.                 {
  820.                     r = 0.0;
  821.                 }
  822.         
  823.                 if( the_value & 0x08000000)    fi -= DegreesToRadians( scale);
  824.                 if( the_value & 0x20000000)    fi += DegreesToRadians( scale);
  825.                 if( the_value & 0x00400000)    fi = 0.0;
  826.  
  827.                 PolarToCartesian( r, fi, &dx, &dy);
  828.  
  829.                 theflow->translation_x = dx;
  830.                 theflow->translation_y = dy;
  831.             } else {
  832.                 if( the_value & 0x00000400)    theflow->translation_x -= d_trans * scale;
  833.                 if( the_value & 0x00100000)    theflow->translation_x += d_trans * scale;
  834.                 if( the_value & 0x02000000)    theflow->translation_x = 0;
  835.         
  836.                 if( the_value & 0x08000000)    theflow->translation_y -= d_trans * scale;
  837.                 if( the_value & 0x20000000)    theflow->translation_y += d_trans * scale;
  838.                 if( the_value & 0x00400000)    theflow->translation_y = 0;
  839.             }
  840.             theflow->changed();
  841.         }
  842.     }
  843.     return escape_is_down;
  844. }
  845.  
  846. void FlipItem( DialogPtr myDialog, short theItem)
  847. {
  848.     short            itemType;
  849.     ControlHandle    itemHandle;
  850.     Rect            itemBox;
  851.     GetDialogItem( myDialog, theItem, &itemType, (Handle *)&itemHandle, &itemBox);
  852.     SetControlValue( itemHandle, 1 - GetControlValue( itemHandle));
  853. }
  854.  
  855. short GetItemValue( const DialogPtr myDialog, short theItem)
  856. {
  857.     short            itemType;
  858.     ControlHandle    itemHandle;
  859.     Rect            itemBox;
  860.     GetDialogItem( myDialog, theItem, &itemType, (Handle *)&itemHandle, &itemBox);
  861.     return GetControlValue( itemHandle);
  862. }
  863.  
  864. void SetItemValue( DialogPtr myDialog, short theItem, short value)
  865. {
  866.     short            itemType;
  867.     ControlHandle    itemHandle;
  868.     Rect            itemBox;
  869.     GetDialogItem( myDialog, theItem, &itemType, (Handle *)&itemHandle, &itemBox);
  870.     SetControlValue( itemHandle, value);
  871. }
  872.  
  873. long GetItemLong( const DialogPtr myDialog, short theItem)
  874. {
  875.     short            itemType;
  876.     ControlHandle    itemHandle;
  877.     Rect            itemBox;
  878.     GetDialogItem( myDialog, theItem, &itemType, (Handle *)&itemHandle, &itemBox);
  879.  
  880.     Str255 text;
  881.     GetDialogItemText( (Handle)itemHandle, text);
  882.     long result;
  883.     StringToNum( text, &result);
  884.     return result;
  885. }
  886.  
  887. void SetItemLong( DialogPtr myDialog, short theItem, long theValue)
  888. {
  889.     short            itemType;
  890.     ControlHandle    itemHandle;
  891.     Rect            itemBox;
  892.     GetDialogItem( myDialog, theItem, &itemType, (Handle *)&itemHandle, &itemBox);
  893.     
  894.     Str255 text;
  895.     NumToString( theValue, text);
  896.     SetDialogItemText( (Handle)itemHandle, text);
  897. }
  898.  
  899. void showParams( const flowdots &alive, const flowdots &kicking, short xPos)
  900. {
  901.     MoveTo( xPos,  35);
  902.     DrawNumber( alive.expansion);        DrawNumber( kicking.expansion);
  903.     
  904.     MoveTo( xPos,  75);
  905.     DrawNumber( alive.rotation);        DrawNumber( kicking.rotation);
  906.  
  907.     MoveTo( xPos, 115);
  908.     DrawNumber( alive.shear_magnitude);    DrawNumber( kicking.shear_magnitude);
  909.     
  910.     MoveTo( xPos, 155);
  911.     DrawNumber( alive.shear_direction);    DrawNumber( kicking.shear_direction);
  912.     
  913.     MoveTo( xPos, 195);
  914.     DrawNumber( alive.translation_x);    DrawNumber( kicking.translation_x);
  915.     
  916.     MoveTo( xPos, 235);
  917.     DrawNumber( alive.translation_y);    DrawNumber( kicking.translation_y);
  918. }
  919.  
  920. void showLegends( short xPos)
  921. {
  922.     MoveTo( xPos,  20);
  923.     DrawString( "\pdiv (/sec)");
  924.     
  925.     MoveTo( xPos,  60);
  926.     DrawString( "\pcurl (°/sec)");
  927.  
  928.     MoveTo( xPos, 100);
  929.     DrawString( "\pshear (/sec)");
  930.     
  931.     MoveTo( xPos, 140);
  932.     DrawString( "\pshear dir (°)");
  933.     
  934.     MoveTo( xPos, 180);
  935.     DrawString( "\pTranslation.x");
  936.     
  937.     MoveTo( xPos, 220);
  938.     DrawString( "\pTranslation.y");
  939. }
  940.  
  941. void DrawNumber( double theNumber)
  942. {
  943.     DrawNumber( (long)(1000.0 * theNumber), 3);
  944. }
  945.  
  946. #define help_the_optimizer
  947.  
  948. void DrawNumber( long theNumber, int implicitDecimal)
  949. {
  950.     #define MAXLENGTH 8
  951.     
  952.     assert( implicitDecimal < MAXLENGTH - 3);
  953.     
  954.     unsigned char string[ MAXLENGTH + 1];
  955.     string[ 0] = MAXLENGTH;
  956.     
  957.     const unsigned char digits[ 11] = "0123456789";
  958.  
  959.     #ifdef help_the_optimizer
  960.         unsigned char *currentptr = &string[ MAXLENGTH];
  961.         const unsigned char *currentlimit = &string[ 1];
  962.     #else
  963.         int currentindex = MAXLENGTH;
  964.     #endif
  965.     //
  966.     // remember the sign (C: the most readable language in the world)
  967.     //
  968.     const unsigned char zodiac = " -"[ theNumber < 0];
  969.     
  970.     if( theNumber < 0)
  971.     {
  972.         theNumber = -theNumber;
  973.     }
  974.     //
  975.     // Output all digits up to the decimal point:
  976.     //
  977.     for( int i = 0; i < implicitDecimal; i++)
  978.     {
  979.         const int lastdigit = theNumber % 10;
  980.         #ifdef help_the_optimizer
  981.             *currentptr-- = digits[ lastdigit];
  982.         #else
  983.             string[ currentindex--] = digits[ lastdigit];
  984.         #endif
  985.         theNumber /= 10;
  986.     }
  987.     //
  988.     // then output the decimal point, if needed
  989.     //
  990.     if( implicitDecimal > 0)
  991.     {
  992.         #ifdef help_the_optimizer
  993.             *currentptr-- = '.';
  994.         #else
  995.             string[ currentindex--] = '.';
  996.         #endif
  997.     }
  998.     //
  999.     // Then output all other digits. Output at least one other digit,
  1000.     // and also check whether we run out of space. We did not have to
  1001.     // do that above, since 'implicitDecimal < MAXLENGTH - 3' does hold.
  1002.     //
  1003.     #ifdef help_the_optimizer
  1004.         do
  1005.         {
  1006.             const int lastdigit = theNumber % 10;
  1007.             *currentptr-- = digits[ lastdigit];
  1008.             theNumber /= 10;
  1009.         } while( (theNumber != 0) && (currentptr > currentlimit));
  1010.     #else
  1011.         do
  1012.         {
  1013.             const int lastdigit = theNumber % 10;
  1014.             string[ currentindex--] = digits[ lastdigit];
  1015.             theNumber /= 10;
  1016.         } while( (theNumber != 0) && (currentindex > 1));
  1017.     #endif
  1018.     //
  1019.     // Prepend the sign or a character to signal overflow
  1020.     //
  1021.     #ifdef help_the_optimizer
  1022.         if( theNumber == 0)
  1023.         {
  1024.             *currentptr-- = zodiac;
  1025.         } else {
  1026.             *currentptr-- = '…';
  1027.         }
  1028.     #else
  1029.         if( theNumber == 0)
  1030.         {
  1031.             string[ currentindex--] = zodiac;
  1032.         } else {
  1033.             string[ currentindex--] = '…';
  1034.         }
  1035.     #endif
  1036.     //
  1037.     // And prepend spaces for the rest of the string:
  1038.     //
  1039.     #ifdef help_the_optimizer
  1040.         while( currentptr > string)
  1041.         {
  1042.             *currentptr-- = ' ';
  1043.         }
  1044.     #else
  1045.         while( currentindex > 0)
  1046.         {
  1047.             string[ currentindex--] = ' ';
  1048.         }
  1049.     #endif
  1050.     //
  1051.     // Finally draw the Pascal string we created:
  1052.     //
  1053.     DrawString( string);
  1054. }
  1055.  
  1056. #ifdef help_the_optimizer
  1057.     #undef help_the_optimizer
  1058. #endif
  1059.  
  1060. void Dialog2options( const DialogPtr theDialog,
  1061.                     const opts *default_options, opts *the_options)
  1062. {
  1063.     the_options->numdots_1        = GetItemLong( theDialog, iNumDots1);
  1064.     the_options->numdots_2        = GetItemLong( theDialog, iNumDots2);
  1065.     the_options->num_noise_1    = GetItemLong( theDialog, iNumNoiseDots1);
  1066.     the_options->num_noise_2    = GetItemLong( theDialog, iNumNoiseDots2);
  1067.  
  1068.     the_options->lifetime_1        = GetItemLong( theDialog, iLife1);
  1069.     the_options->lifetime_2        = GetItemLong( theDialog, iLife2);
  1070.     the_options->noise_life_1    = GetItemLong( theDialog, iNoiseLife1);
  1071.     the_options->noise_life_2    = GetItemLong( theDialog, iNoiseLife2);
  1072.  
  1073.     the_options->diffusion_1    = GetItemLong( theDialog, iDiffusion1);
  1074.     the_options->diffusion_2    = GetItemLong( theDialog, iDiffusion2);
  1075.  
  1076.     the_options->switchinterval = GetItemLong( theDialog, iSwitchInterval);
  1077.  
  1078.     for( int itemNo = iTransparant; itemNo <= iSurprise; itemNo++)
  1079.     {
  1080.         if( GetItemValue( theDialog, itemNo) != 0)
  1081.         {
  1082.             the_options->stim_type = itemNo - iTransparant;
  1083.         }
  1084.     }
  1085.  
  1086.     if( GetItemValue( theDialog, i128x128) != 0)
  1087.     {
  1088.         the_options->resolution_type =  7;
  1089.     } else if( GetItemValue( theDialog, i256x256) != 0) {
  1090.         the_options->resolution_type =  8;
  1091.     } else if( GetItemValue( theDialog, i512x512) != 0) {
  1092.         the_options->resolution_type =  9;
  1093.     } else {
  1094.         the_options->resolution_type = 10;
  1095.     }
  1096.  
  1097.     the_options->create_output    = GetItemValue( theDialog, iLogClicks);
  1098.     the_options->fixation_dot    = GetItemValue( theDialog, iFixationDot);
  1099.     the_options->round_frame    = GetItemValue( theDialog, iCircularMask);
  1100.     the_options->flickering        = GetItemValue( theDialog, iFlickering);
  1101.  
  1102.     the_options->d_log            = GetItemLong( theDialog, i_dLog);
  1103.     the_options->d_rot_dir        = GetItemLong( theDialog, i_dRotDir);
  1104.     the_options->d_trans        = GetItemLong( theDialog, i_dTrans);
  1105.     the_options->allow_modifications
  1106.                                 = GetItemValue( theDialog, iAllowModifications);
  1107.  
  1108.     the_options->stepping        = (GetItemValue( theDialog, iStepThrough) != 0);
  1109.     the_options->numtimingloops = default_options->numtimingloops;
  1110. }
  1111.  
  1112. void options2Dialog( const opts *the_options, DialogPtr theDialog)
  1113. {
  1114.     SetItemLong( theDialog, iNumDots1,        the_options->numdots_1);
  1115.     SetItemLong( theDialog, iNumDots2,        the_options->numdots_2);
  1116.     SetItemLong( theDialog, iNumNoiseDots1,    the_options->num_noise_1);
  1117.     SetItemLong( theDialog, iNumNoiseDots2,    the_options->num_noise_2);
  1118.     
  1119.     SetItemLong( theDialog, iLife1,            the_options->lifetime_1);
  1120.     SetItemLong( theDialog, iLife2,            the_options->lifetime_2);
  1121.     SetItemLong( theDialog, iNoiseLife1,    the_options->noise_life_1);
  1122.     SetItemLong( theDialog, iNoiseLife2,    the_options->noise_life_2);
  1123.  
  1124.     SetItemLong( theDialog, iDiffusion1,    the_options->diffusion_1);
  1125.     SetItemLong( theDialog, iDiffusion2,    the_options->diffusion_2);
  1126.     
  1127.     SetItemLong( theDialog, iSwitchInterval, the_options->switchinterval);
  1128.  
  1129.     const int theItemToSet = the_options->stim_type + iTransparant;
  1130.     
  1131.     for( int itemNo = iTransparant; itemNo <= iSurprise; itemNo++)
  1132.     {
  1133.         SetItemValue( theDialog, itemNo, (theItemToSet == itemNo));
  1134.     }
  1135.  
  1136.     SetItemValue( theDialog, i128x128,   (the_options->resolution_type ==  7));
  1137.     SetItemValue( theDialog, i256x256,   (the_options->resolution_type ==  8));
  1138.     SetItemValue( theDialog, i512x512,   (the_options->resolution_type ==  9));
  1139.     SetItemValue( theDialog, i1024x1024, (the_options->resolution_type == 10));
  1140.  
  1141.     SetItemValue( theDialog, iLogClicks,    the_options->create_output);
  1142.     SetItemValue( theDialog, iFixationDot,    the_options->fixation_dot);
  1143.     SetItemValue( theDialog, iCircularMask,    the_options->round_frame);
  1144.     SetItemValue( theDialog, iFlickering,    the_options->flickering);
  1145.  
  1146.     SetItemLong( theDialog, i_dLog, the_options->d_log);
  1147.     SetItemLong( theDialog, i_dRotDir, the_options->d_rot_dir);
  1148.     SetItemLong( theDialog, i_dTrans, the_options->d_trans);
  1149.     
  1150.     SetItemValue( theDialog, iAllowModifications, the_options->allow_modifications);
  1151.     SetItemValue( theDialog, iStepThrough, the_options->stepping);
  1152.     
  1153.     UpdateSensitivityItems( theDialog, the_options->allow_modifications);
  1154. }
  1155.  
  1156. void UpdateSensitivityItems( DialogPtr theDialog, long allow_modifications)
  1157. {
  1158.     //
  1159.     // this routine should use some kind of table, but it works…
  1160.     //
  1161.     if( allow_modifications)
  1162.     {
  1163.         ShowDialogItem( theDialog, i_dLog);
  1164.         ShowDialogItem( theDialog, i_dTrans);
  1165.         ShowDialogItem( theDialog, i_dRotDir);
  1166.         
  1167.         HideDialogItem( theDialog, i_dLogStatic);
  1168.         HideDialogItem( theDialog, i_dTransStatic);
  1169.         HideDialogItem( theDialog, i_dRotDirStatic);
  1170.     } else {
  1171.         HideDialogItem( theDialog, i_dLog);
  1172.         HideDialogItem( theDialog, i_dTrans);
  1173.         HideDialogItem( theDialog, i_dRotDir);
  1174.         
  1175.         SetItemLong( theDialog, i_dLogStatic,        GetItemLong( theDialog, i_dLog));
  1176.         SetItemLong( theDialog, i_dTransStatic,        GetItemLong( theDialog, i_dTrans));
  1177.         SetItemLong( theDialog, i_dRotDirStatic,    GetItemLong( theDialog, i_dRotDir));
  1178.         
  1179.         ShowDialogItem( theDialog, i_dLogStatic);
  1180.         ShowDialogItem( theDialog, i_dTransStatic);
  1181.         ShowDialogItem( theDialog, i_dRotDirStatic);
  1182.     }
  1183. }
  1184.  
  1185. void PolarToCartesian( double r, double fi, double *x, double *y)
  1186. {
  1187.     *x = r * cos( fi);
  1188.     *y = r * sin( fi);
  1189. }
  1190.  
  1191. void CartesianToPolar( double x, double y, double *r, double *fi)
  1192. {
  1193.     //
  1194.     // 950614: hacking 'hypot' in. It is defined in fp.h, but leads to
  1195.     // link errors => using a crude macro equivalent
  1196.     // We don't have to worry about overflow here.
  1197.     //
  1198.     #define hypot(a,b) sqrt( a * a + b * b)
  1199.         *r  = hypot( x, y);
  1200.         *fi = atan2( y, x);
  1201.     #undef hypot
  1202. }
  1203.